/*
 * Copyright (C) 2012 Компания ВИТ. web: http://diasled.ru Е-mail:  vit@diasled.ru
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ru.apertum.diasled.model.exam;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import ru.apertum.diasled.model.Examination;

/**
 *
 * @author Evgeniy Egorov
 */
public class GinCanvas extends Canvas implements MouseMotionListener, MouseListener {

    private Examination exam = null;
    int max = 0;
    private ArrayList<Integer>[] steps = null;
    private boolean[][] ginElems = null;

    public void setGinElems(boolean[][] ginElems) {
        this.ginElems = ginElems;
    }
    private int[][] data = null;

    public int[][] getData() {
        return data;
    }

    public void setData(int[][] data, Examination exam) {
        this.data = data;
        this.exam = exam;
        max = 0;
        for (int i = 0; i < data.length; i++) {
            for (int j = 0; j < data[i].length; j++) {
                final int is = data[i][j];
                if (is > max) {
                    max = is;
                }
            }
        }

        steps = new ArrayList<>[data[0].length];
        final float dd = 0.10f;
        int dm = Math.round(max * dd);
        final int d_sum = 10;
        final int spr_nxt = Math.round(1f / exam.getStepsРerMinute() / 60 * 100 / 3);
        for (int j = 0; j < data[0].length; j++) { // бежим по всем контурам
            steps[j] = new ArrayList<>();
            int avg_old = 0;
            int sum = 0;
            int spr = -1;
            for (int i = 0; i < data.length; i++) {// бежим по крафику контура
                sum = sum + data[i][j];
                final int avg;// среднее арифметическое d_sum подрядидущих значений
                if (i - d_sum > -1) {
                    sum = sum - data[i - d_sum][j];
                    avg = sum / d_sum;
                } else {
                    avg = sum / (i + 1);
                }
                int pos = i - d_sum / 2;
                if (pos > spr && avg > dm && avg_old <= dm) {
                    spr = spr + spr_nxt; // перепрыгним несколько значений графика чтоб в шум не попасть рядом с точкой начала шага
                    while (pos > 0 && (float) (data[pos + 1][j] - data[pos][j]) / (data[pos][j] - data[pos - 1][j] + 0.001) >= .3f) {
                        pos--;
                    }
                    steps[j].add(pos);
                }
                avg_old = avg;
            }
        }
    }
    private float kx = 1;
    private float ky = 1;

    @Override
    public void paint(Graphics g) {
        final BufferedImage bi = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_3BYTE_BGR);
        final Graphics2D g2 = bi.createGraphics();
        g2.setColor(Color.white);
        g2.fillRect(0, 0, getWidth(), getHeight());

        kx = (float) (getWidth() - 10 + scaleX) / data.length;
        ky = (float) (getHeight() - 10 + scaleY) / max;
        g2.translate(dx, getHeight() - 1 + dy);
        // g2.scale(kx, ky);

        final int stt = Math.round((0 - dx) / kx) - 10;
        final int fin = Math.round((getWidth() - dx) / kx) + 10;

        // рисуем ломаную
        for (int j = 0; j < data[0].length; j++) {
            if (ginElems == null || ginElems[0][j]) {
                
                // это выставление цведа для контуров
                String s = "0000000" + Integer.toBinaryString(j + 1);
                s = s.substring(s.length() - 3);
                g2.setColor(new Color(255 * Integer.parseInt(s.substring(2)), 255 * Integer.parseInt(s.substring(0, 1)), 255 * Integer.parseInt(s.substring(1, 2))));
                
                 // это вешки шагов
                if (ginElems == null || ginElems[1][j]) {
                    for (int i : steps[j]) {
                        if (i > stt && i < fin) {
                            //g2.drawLine(Math.round(i * kx), 250, Math.round(i * kx), -getHeight() - dy);
                            for (int k = -getHeight() - dy; k < 250; k=k+20) {
                                g2.drawLine(Math.round(i * kx), k, Math.round(i * kx), k-15);
                                
                            }
                        }
                    }
                }
                
                // это гин контуров
                for (int i = 1; i < data.length; i++) {
                    if (i > stt && i < fin) {
                        g2.drawLine(Math.round((i - 1) * kx), Math.round(-data[i - 1][j] * ky), Math.round(i * kx), Math.round(-data[i][j] * ky));
                    }
                }
               
            }
        }
        ((Graphics2D) g).drawImage(bi, null, 0, 0);
    }

    @Override
    public void update(Graphics g) {
        paint(getGraphics());
    }
    private IGinClick ginClick;

    public IGinClick getGinClick() {
        return ginClick;
    }

    public void setGinClick(IGinClick ginClick) {
        this.ginClick = ginClick;
    }

    @Override
    public void mouseDragged(MouseEvent e) {
        if (dragg == 1) {
            setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
            dx = -sx + e.getX();
            dy = -sy + e.getY();
            java.awt.EventQueue.invokeLater(new Runnable() {

                public void run() {
                    repaint();
                }
            });
        }
        if (dragg == 2) {
            dragRoute.set(posDragRoute, Math.round((e.getX() - dx) / kx));
            java.awt.EventQueue.invokeLater(new Runnable() {

                public void run() {
                    paint(getGraphics());
                }
            });
        }
    }
    private ArrayList<Integer> dragRoute;
    private int posDragRoute = -1;

    @Override
    public void mouseMoved(MouseEvent e) {
        for (int j = 0; j < steps.length; j++) {
            final ArrayList<Integer> al = steps[j];
            if (ginElems != null && !(ginElems[1][j] && ginElems[0][j])) {
                continue;
            }
            int ii = 0;
            for (int i : al) {
                if (Math.abs(e.getX() - i * kx - dx) < 2) {
                    setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR));
                    dragRoute = al;
                    posDragRoute = ii;
                    return;
                }
                ii++;
            }
        }
        posDragRoute = -1;
        setCursor(Cursor.getDefaultCursor());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        final int pos = Math.round((e.getX() - dx) / kx);
        if (ginClick != null && pos > 0 && pos < data.length) {
            ginClick.mouseClicked(e, pos);
        }
        if (e.getButton() == MouseEvent.BUTTON3) {
            if (posDragRoute > 0) { // если над вешкой, то удалим ее по правому клику
                dragRoute.remove(posDragRoute);
                posDragRoute = -1;
            } else { // иначе добавим новую вешку по правому клику
                int min = 100000;
                int st = 0;
                for (int i = 0; i < data[0].length; i++) {

                    int d = Math.abs(data[Math.round((e.getX() - dx) / kx)][i] - Math.round(e.getY() * ky));
                    if (min > d) {
                        min = d;
                        st = i;
                    }
                }
                steps[st].add(Math.round((e.getX() - dx) / kx));
            }
            // ну и перересуем
            java.awt.EventQueue.invokeLater(new Runnable() {

                public void run() {
                    repaint();
                }
            });
            return;
        }
    }

    @Override
    public void mousePressed(MouseEvent e) {
        if (e.getButton() == MouseEvent.BUTTON1) {
            sx = e.getPoint().x - dx;
            sy = e.getPoint().y - dy;
            dragg = (posDragRoute == -1 ? 1 : 2);
            return;
        }
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        setCursor(Cursor.getDefaultCursor());
        dragg = 0;
    }

    @Override
    public void mouseEntered(MouseEvent e) {
    }

    @Override
    public void mouseExited(MouseEvent e) {
    }
    int sx, sy, dx, dy;

    public int getDy() {
        return dy;
    }

    public void setDy(int dy) {
        this.dy = dy;
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                repaint();
            }
        });
    }

    public int getDx() {
        return dx;
    }

    public void setDx(int dx) {
        this.dx = dx;
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                repaint();
            }
        });
    }
    /**
     * 0-ничего, 1-тянем график, 2-тянем вешку шага
     */
    int dragg = 0;
    private int scaleX = 1;

    public int getScaleX() {
        return scaleX;
    }

    public void setScaleX(int scaleX) {
        this.scaleX = scaleX;
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                repaint();
            }
        });
    }
    private int scaleY = 1;

    public int getScaleY() {
        return scaleY;
    }

    public void setScaleY(int scaleY) {
        this.scaleY = scaleY;
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                repaint();
            }
        });
    }
}
